package net.minecraft.entity;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import net.minecraft.crash.CrashReport;
import net.minecraft.crash.CrashReportCategory;
import net.minecraft.entity.boss.EntityDragon;
import net.minecraft.entity.boss.EntityWither;
import net.minecraft.entity.item.*;
import net.minecraft.entity.passive.EntityBat;
import net.minecraft.entity.passive.EntitySquid;
import net.minecraft.entity.passive.IAnimals;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.projectile.*;
import net.minecraft.network.Packet;
import net.minecraft.util.IntHashMap;
import net.minecraft.util.ReportedException;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Callable;

public class EntityTracker {
    private static final Logger logger = LogManager.getLogger();
    private final WorldServer theWorld;

    /**
     * List of tracked entities, used for iteration operations on tracked entities.
     */
    private Set trackedEntities = Sets.newHashSet();

    /**
     * Used for identity lookup of tracked entities.
     */
    private IntHashMap trackedEntityHashTable = new IntHashMap();
    private int maxTrackingDistanceThreshold;
    // private static final String __OBFID = "CL_00001431";

    public EntityTracker(WorldServer p_i1516_1_) {
        this.theWorld = p_i1516_1_;
        this.maxTrackingDistanceThreshold = p_i1516_1_.func_73046_m().getConfigurationManager().getEntityViewDistance();
    }

    public void trackEntity(Entity p_72786_1_) {
        if (p_72786_1_ instanceof EntityPlayerMP) {
            this.trackEntity(p_72786_1_, 512, 2);
            EntityPlayerMP var2 = (EntityPlayerMP) p_72786_1_;
            Iterator var3 = this.trackedEntities.iterator();

            while (var3.hasNext()) {
                EntityTrackerEntry var4 = (EntityTrackerEntry) var3.next();

                if (var4.trackedEntity != var2) {
                    var4.updatePlayerEntity(var2);
                }
            }
        } else if (p_72786_1_ instanceof EntityFishHook) {
            this.addEntityToTracker(p_72786_1_, 64, 5, true);
        } else if (p_72786_1_ instanceof EntityArrow) {
            this.addEntityToTracker(p_72786_1_, 64, 20, false);
        } else if (p_72786_1_ instanceof EntitySmallFireball) {
            this.addEntityToTracker(p_72786_1_, 64, 10, false);
        } else if (p_72786_1_ instanceof EntityFireball) {
            this.addEntityToTracker(p_72786_1_, 64, 10, false);
        } else if (p_72786_1_ instanceof EntitySnowball) {
            this.addEntityToTracker(p_72786_1_, 64, 10, true);
        } else if (p_72786_1_ instanceof EntityEnderPearl) {
            this.addEntityToTracker(p_72786_1_, 64, 10, true);
        } else if (p_72786_1_ instanceof EntityEnderEye) {
            this.addEntityToTracker(p_72786_1_, 64, 4, true);
        } else if (p_72786_1_ instanceof EntityEgg) {
            this.addEntityToTracker(p_72786_1_, 64, 10, true);
        } else if (p_72786_1_ instanceof EntityPotion) {
            this.addEntityToTracker(p_72786_1_, 64, 10, true);
        } else if (p_72786_1_ instanceof EntityExpBottle) {
            this.addEntityToTracker(p_72786_1_, 64, 10, true);
        } else if (p_72786_1_ instanceof EntityFireworkRocket) {
            this.addEntityToTracker(p_72786_1_, 64, 10, true);
        } else if (p_72786_1_ instanceof EntityItem) {
            this.addEntityToTracker(p_72786_1_, 64, 20, true);
        } else if (p_72786_1_ instanceof EntityMinecart) {
            this.addEntityToTracker(p_72786_1_, 80, 3, true);
        } else if (p_72786_1_ instanceof EntityBoat) {
            this.addEntityToTracker(p_72786_1_, 80, 3, true);
        } else if (p_72786_1_ instanceof EntitySquid) {
            this.addEntityToTracker(p_72786_1_, 64, 3, true);
        } else if (p_72786_1_ instanceof EntityWither) {
            this.addEntityToTracker(p_72786_1_, 80, 3, false);
        } else if (p_72786_1_ instanceof EntityBat) {
            this.addEntityToTracker(p_72786_1_, 80, 3, false);
        } else if (p_72786_1_ instanceof EntityDragon) {
            this.addEntityToTracker(p_72786_1_, 160, 3, true);
        } else if (p_72786_1_ instanceof IAnimals) {
            this.addEntityToTracker(p_72786_1_, 80, 3, true);
        } else if (p_72786_1_ instanceof EntityTNTPrimed) {
            this.addEntityToTracker(p_72786_1_, 160, 10, true);
        } else if (p_72786_1_ instanceof EntityFallingBlock) {
            this.addEntityToTracker(p_72786_1_, 160, 20, true);
        } else if (p_72786_1_ instanceof EntityHanging) {
            this.addEntityToTracker(p_72786_1_, 160, Integer.MAX_VALUE, false);
        } else if (p_72786_1_ instanceof EntityArmorStand) {
            this.addEntityToTracker(p_72786_1_, 160, 3, true);
        } else if (p_72786_1_ instanceof EntityXPOrb) {
            this.addEntityToTracker(p_72786_1_, 160, 20, true);
        } else if (p_72786_1_ instanceof EntityEnderCrystal) {
            this.addEntityToTracker(p_72786_1_, 256, Integer.MAX_VALUE, false);
        }
    }

    public void trackEntity(Entity p_72791_1_, int p_72791_2_, int p_72791_3_) {
        this.addEntityToTracker(p_72791_1_, p_72791_2_, p_72791_3_, false);
    }

    /**
     * Args : Entity, trackingRange, updateFrequency, sendVelocityUpdates
     */
    public void addEntityToTracker(Entity p_72785_1_, int p_72785_2_, final int p_72785_3_, boolean p_72785_4_) {
        if (p_72785_2_ > this.maxTrackingDistanceThreshold) {
            p_72785_2_ = this.maxTrackingDistanceThreshold;
        }

        try {
            if (this.trackedEntityHashTable.containsItem(p_72785_1_.getEntityId())) {
                throw new IllegalStateException("Entity is already tracked!");
            }

            EntityTrackerEntry var5 = new EntityTrackerEntry(p_72785_1_, p_72785_2_, p_72785_3_, p_72785_4_);
            this.trackedEntities.add(var5);
            this.trackedEntityHashTable.addKey(p_72785_1_.getEntityId(), var5);
            var5.updatePlayerEntities(this.theWorld.playerEntities);
        } catch (Throwable var11) {
            CrashReport var6 = CrashReport.makeCrashReport(var11, "Adding entity to track");
            CrashReportCategory var7 = var6.makeCategory("Entity To Track");
            var7.addCrashSection("Tracking range", p_72785_2_ + " blocks");
            var7.addCrashSectionCallable("Update interval", new Callable() {
                // private static final String __OBFID = "CL_00001432";
                public String call() {
                    String var1 = "Once per " + p_72785_3_ + " ticks";

                    if (p_72785_3_ == Integer.MAX_VALUE) {
                        var1 = "Maximum (" + var1 + ")";
                    }

                    return var1;
                }
            });
            p_72785_1_.addEntityCrashInfo(var7);
            CrashReportCategory var8 = var6.makeCategory("Entity That Is Already Tracked");
            ((EntityTrackerEntry) this.trackedEntityHashTable.lookup(p_72785_1_.getEntityId())).trackedEntity.addEntityCrashInfo(var8);

            try {
                throw new ReportedException(var6);
            } catch (ReportedException var10) {
                logger.error("\"Silently\" catching entity tracking error.", var10);
            }
        }
    }

    public void untrackEntity(Entity p_72790_1_) {
        if (p_72790_1_ instanceof EntityPlayerMP) {
            EntityPlayerMP var2 = (EntityPlayerMP) p_72790_1_;
            Iterator var3 = this.trackedEntities.iterator();

            while (var3.hasNext()) {
                EntityTrackerEntry var4 = (EntityTrackerEntry) var3.next();
                var4.removeFromTrackedPlayers(var2);
            }
        }

        EntityTrackerEntry var5 = (EntityTrackerEntry) this.trackedEntityHashTable.removeObject(p_72790_1_.getEntityId());

        if (var5 != null) {
            this.trackedEntities.remove(var5);
            var5.sendDestroyEntityPacketToTrackedPlayers();
        }
    }

    public void updateTrackedEntities() {
        ArrayList var1 = Lists.newArrayList();
        Iterator var2 = this.trackedEntities.iterator();

        while (var2.hasNext()) {
            EntityTrackerEntry var3 = (EntityTrackerEntry) var2.next();
            var3.updatePlayerList(this.theWorld.playerEntities);

            if (var3.playerEntitiesUpdated && var3.trackedEntity instanceof EntityPlayerMP) {
                var1.add((EntityPlayerMP) var3.trackedEntity);
            }
        }

        for (int var6 = 0; var6 < var1.size(); ++var6) {
            EntityPlayerMP var7 = (EntityPlayerMP) var1.get(var6);
            Iterator var4 = this.trackedEntities.iterator();

            while (var4.hasNext()) {
                EntityTrackerEntry var5 = (EntityTrackerEntry) var4.next();

                if (var5.trackedEntity != var7) {
                    var5.updatePlayerEntity(var7);
                }
            }
        }
    }

    public void func_180245_a(EntityPlayerMP p_180245_1_) {
        Iterator var2 = this.trackedEntities.iterator();

        while (var2.hasNext()) {
            EntityTrackerEntry var3 = (EntityTrackerEntry) var2.next();

            if (var3.trackedEntity == p_180245_1_) {
                var3.updatePlayerEntities(this.theWorld.playerEntities);
            } else {
                var3.updatePlayerEntity(p_180245_1_);
            }
        }
    }

    public void sendToAllTrackingEntity(Entity p_151247_1_, Packet p_151247_2_) {
        EntityTrackerEntry var3 = (EntityTrackerEntry) this.trackedEntityHashTable.lookup(p_151247_1_.getEntityId());

        if (var3 != null) {
            var3.func_151259_a(p_151247_2_);
        }
    }

    public void func_151248_b(Entity p_151248_1_, Packet p_151248_2_) {
        EntityTrackerEntry var3 = (EntityTrackerEntry) this.trackedEntityHashTable.lookup(p_151248_1_.getEntityId());

        if (var3 != null) {
            var3.func_151261_b(p_151248_2_);
        }
    }

    public void removePlayerFromTrackers(EntityPlayerMP p_72787_1_) {
        Iterator var2 = this.trackedEntities.iterator();

        while (var2.hasNext()) {
            EntityTrackerEntry var3 = (EntityTrackerEntry) var2.next();
            var3.removeTrackedPlayerSymmetric(p_72787_1_);
        }
    }

    public void func_85172_a(EntityPlayerMP p_85172_1_, Chunk p_85172_2_) {
        Iterator var3 = this.trackedEntities.iterator();

        while (var3.hasNext()) {
            EntityTrackerEntry var4 = (EntityTrackerEntry) var3.next();

            if (var4.trackedEntity != p_85172_1_ && var4.trackedEntity.chunkCoordX == p_85172_2_.xPosition && var4.trackedEntity.chunkCoordZ == p_85172_2_.zPosition) {
                var4.updatePlayerEntity(p_85172_1_);
            }
        }
    }
}
